import gobject, gtk
import threading, os, httplib
from simple_parser import SimpleParser
import logging

class FileDownload(threading.Thread, gobject.GObject):
    __gsignals__ = {
        "file-downloaded" : (
            gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_STRING]),
        "chunk-downloaded" : (
            gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_STRING]),
        }
    def __init__(self, ip, port, path, destination_dir):
        threading.Thread.__init__(self)
        gobject.GObject.__init__(self)

        self.ip = ip
        self.port = port
        self.path = path
        name = os.path.basename(path)
        if len(name):
            self.dest = file( os.path.join(destination_dir, name), 'w')
        else:
            self.dest = file( os.tempnam(destination_dir), 'w')

    def run(self):
        logging.info("Download thread running")
        self.con = httplib.HTTPConnection(self.ip, self.port)
        self.con.request("GET", self.path)
        self.resp = self.con.getresponse()
        
        for chunk in range(0, self.resp.length, 1024):
            next = self.resp.read(1024)
            self.dest.write(next) 
            self.emit('chunk-downloaded',self.path)
        
        self.dest.close()
        self.emit('file-downloaded', self.path)

    def emit(self, *args):
        gobject.idle_add(gobject.GObject.emit,self,*args)

class ServerQuery(threading.Thread, gobject.GObject):
    __gsignals__ = {
        "download-completed" : (
            gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [gobject.TYPE_PYOBJECT]),
        }
    def __init__(self, address, ip, port, *args):
        threading.Thread.__init__(self)
        gobject.GObject.__init__(self)

        self.address = address
        self.ip = ip
        self.port = port
        self.paths = None
        self.piter = args[0]

    def run(self):
        con = httplib.HTTPConnection(self.address, self.port)
        con.request('GET','/')
        content = con.getresponse() 

        try:
            ps = SimpleParser(content)
            ps.parse()
            self.paths = ps.getPaths()
            self.emit("download-completed", self.paths)
        except ExpatError, e:
            logging.warning('parsing failed'+str(e))
        except ParseError, e:
            logging.warning('parsing failed'+str(e))

    def emit(self, *args):
        gobject.idle_add(gobject.GObject.emit,self,*args)

class DownloadManager():
    def __init__(self, maxThreads):
        self.maxThreads = maxThreads
        self.threads = {}
        self.pendingThreads = []
        self.running = 0

    def _thread_completed(self, thread, *args):
        self.running -= 1
        try:
            del(self.threads[(thread.ip, thread.port, thread.path)])
            logging.info("thing deleted")
        except KeyError, e:
            logging.debug('thread %s not found to delete. ??' % str(thread))
        if self.running < self.maxThreads and self.pendingThreads:
            self.pendingThreads.pop().start()
            self.running += 1

    def add_download(self, where, to, callback, callback_chunk):
        if self.threads.get(where) is None:
            nt = FileDownload(where[0], where[1], where[2], to)
            self.threads[where] = nt
            nt.connect("file-downloaded", callback, [])
            nt.connect("chunk-downloaded", callback_chunk, [])
            nt.connect("file-downloaded", self._thread_completed, [])
            #nt.connect("chunk-completed", chunk_callback, [])
        
            self.pendingThreads.append(nt)

            if self.running < self.maxThreads:
                self.pendingThreads.pop().start()
                logging.info('thread started')
                self.running += 1
        
class QueryManager():
    def __init__(self, maxThreads):
        self.maxThreads = maxThreads
        self.threads = {}
        self.pendingThreads = []
        self.running = 0

    def _thread_completed(self, thread, *args):
        self.running -= 1
        try:
            del(self.threads[(thread.address, thread.ip, thread.port)])
        except KeyError, e:
            logging.debug('thread %s not found to delete. ??' % str(thread))
        if self.running < self.maxThreads and self.pendingThreads:
            self.pendingThreads.pop().start()
            self.running += 1

    def add_server_query(self, where, callback):
        if self.threads.get(where) is None:
            nt = ServerQuery(where[0], where[1], where[2], where[3])
            self.threads[where] = nt
            nt.connect("download-completed", callback, [])
            nt.connect("download-completed", self._thread_completed, [])

            self.pendingThreads.append(nt)

            if self.running < self.maxThreads:
                self.pendingThreads.pop().start()
                self.running += 1
